Structural and Cohesion Fund beneficiaries: France (2014-2020)

For background information on the nature of the funds, see the Wikipedia page.

Load the data locally

In [1]:
from pandas import read_csv
In [2]:
# The first 5 rows are headers (French and English) + junk

df = read_csv('/home/loic/Downloads/france-openspending.csv', 
              names=['program_name',
                    'project_name',
                    'recipient',
                    'recipient_postcode',
                    'project_start_date',
                    'project_end_date',
                    'amount_eligible',
                    'amount_provisionned',
                    'operation_postcode',
                    'zone',
                    'district', 
                    'region',
                    'country',
                    'funding_category',
                    'acceptance_date',
                    'fund']).ix[5:].reset_index(drop=True)
df.head()
Out[2]:
program_name project_name recipient recipient_postcode project_start_date project_end_date amount_eligible amount_provisionned operation_postcode zone district region country funding_category acceptance_date fund
0 P.O Auvergne Atelier sectoriel industrie (GIP FCIP Auvergne ) Conseil régional d'Auvergne 63050 6/5/2014 6/5/2016 130,455.00 € 97,841.25 € NaN NaN 63 Issoire FR CI01_103 - Intégration durable sur le marché d... 12/12/2014 IEJ
1 P.O Auvergne Formation de magasinier cariste (CRCIA) Conseil régional d'Auvergne 63050 NaN NaN 38,675.00 € 29,006.25 € NaN NaN 3 Vichy FR CI01_103 - Intégration durable sur le marché d... 12/12/2014 IEJ
2 P.O Auvergne Maintenance industriel (GIP) Conseil régional d'Auvergne 63050 1/7/2014 1/7/2016 286,000.00 € 214,500.00 € NaN NaN 63 Issoire/ Beaumont/ Romagnat FR CI01_103 - Intégration durable sur le marché d... 12/12/2014 IEJ
3 P.O Auvergne Maintenance industrielle (GIP) Conseil régional d'Auvergne 63050 NaN NaN 200,200.00 € 150,150.00 € NaN NaN 43/63 Yssingeaux/ Puy-en-Velay/ Brioude/ Issoire FR CI01_103 - Intégration durable sur le marché d... 12/12/2014 IEJ
4 P.O Auvergne Atelier sectoriel bois / lot scierie (EPLEFPA ... Conseil régional d'Auvergne 63050 NaN NaN 184,548.00 € 138,411.00 € NaN NaN 63 Ambert FR CI01_103 - Intégration durable sur le marché d... 12/12/2014 IEJ

Data scrubbing

Dates

In [3]:
from pandas import to_datetime

date_columns = ['project_start_date', 'project_end_date', 'acceptance_date']

for column in date_columns:
    df[column] = to_datetime(df[column])
                            
df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 505 entries, 0 to 504
Data columns (total 16 columns):
program_name           505 non-null object
project_name           504 non-null object
recipient              505 non-null object
recipient_postcode     488 non-null object
project_start_date     383 non-null datetime64[ns]
project_end_date       80 non-null datetime64[ns]
amount_eligible        504 non-null object
amount_provisionned    505 non-null object
operation_postcode     8 non-null object
zone                   0 non-null object
district               474 non-null object
region                 466 non-null object
country                490 non-null object
funding_category       455 non-null object
acceptance_date        202 non-null datetime64[ns]
fund                   501 non-null object
dtypes: datetime64[ns](3), object(13)
memory usage: 63.2+ KB

Currency

In [4]:
def parse_euros(amount):
    return float(amount.replace(' €', '').replace(',', ''))

# Try the function out
print(parse_euros('286,000.00 €'), '\n')

currency_columns = ['amount_eligible', 'amount_provisionned']

for column in currency_columns:
    # The 'amount_eligible' colummn has one float value: 
    df[column] = df[column].apply(str).apply(parse_euros)
    
df.info()
286000.0 

<class 'pandas.core.frame.DataFrame'>
RangeIndex: 505 entries, 0 to 504
Data columns (total 16 columns):
program_name           505 non-null object
project_name           504 non-null object
recipient              505 non-null object
recipient_postcode     488 non-null object
project_start_date     383 non-null datetime64[ns]
project_end_date       80 non-null datetime64[ns]
amount_eligible        504 non-null float64
amount_provisionned    505 non-null float64
operation_postcode     8 non-null object
zone                   0 non-null object
district               474 non-null object
region                 466 non-null object
country                490 non-null object
funding_category       455 non-null object
acceptance_date        202 non-null datetime64[ns]
fund                   501 non-null object
dtypes: datetime64[ns](3), float64(2), object(11)
memory usage: 63.2+ KB

Basic insights

Subsidy amounts

In [5]:
from pandas import options
options.display.float_format = '{:,.0f} K€'.format

amounts = df[['amount_eligible', 'amount_provisionned']].apply(lambda x: x / 1000)
amounts.describe().ix[1:]
Out[5]:
amount_eligible amount_provisionned
mean 839 K€ 355 K€
std 2,107 K€ 1,042 K€
min 0 K€ 0 K€
25% 145 K€ 68 K€
50% 288 K€ 143 K€
75% 743 K€ 277 K€
max 29,295 K€ 17,324 K€
In [6]:
print('total eligible: %.0f M€' % (amounts['amount_eligible'].sum() / 1000))
print('total provisionned: %.0f M€' % (amounts['amount_provisionned'].sum() / 1000))
total eligible: 423 M€
total provisionned: 179 M€

Take away points:

  • Projects get 1/3 of what they are theoretically eligible for
  • The smaller projects get < 100,000 €
  • The median project gets 300 K€
  • The are a few very large projects > 15 M€

Who is getting the biggest subsidies ?

top subsidies

In [7]:
sorted_amounts = amounts.sort_values(by='amount_provisionned', ascending=False)
sorted_amounts.head(10)
Out[7]:
amount_eligible amount_provisionned
238 29,295 K€ 17,324 K€
458 19,042 K€ 9,521 K€
232 nan K€ 6,829 K€
420 19,963 K€ 5,000 K€
301 9,000 K€ 4,500 K€
237 6,399 K€ 3,708 K€
478 4,599 K€ 3,416 K€
233 6,285 K€ 2,968 K€
406 5,500 K€ 2,750 K€
75 7,668 K€ 2,556 K€
In [8]:
df[['recipient', 'amount_provisionned', 'project_name', 'fund']].ix[sorted_amounts.head(10).index]
Out[8]:
recipient amount_provisionned project_name fund
238 Centre Hospitalier de Mayotte 17,324,000 K€ Construction de l'hôpital de Petite de Terre FEDER
458 REGION DE MIDI PYRENEES 9,520,967 K€ SOUTIEN AUX ACTIONS DE FORMATION S'INSCRIVANT ... FSE
232 Etablissement Public d'Aménagement en Guyane 6,828,745 K€ GUY-Aménagement des VRD primaires de la ZAC Ec... FEDER
420 MKAD 5,000,000 K€ 2015 - MKAD - GRAND PROJET INDUSTRIEL FEDER
301 REGION DE MIDI PYRENEES 4,500,000 K€ ASTER - DEPLOIEMENT THD POUR LES COMMUNAUTES D... FEDER
237 Agence de Services et de Paiement - ASP 3,708,329 K€ SYNERGIE - Conception, développement et TMA du SI FEDER
478 Université de Franche-Comté 3,416,390 K€ 11001 3S-MEMS FEDER
233 Ministère de l'Education Nationale de l'Enseig... 2,968,000 K€ Construction du bâtiment du restaurant univers... FEDER
406 REGION DE MIDI PYRENEES 2,750,000 K€ MISE A DISPOSITION D'UNE PLATFEORME DE SERVICE... FEDER
75 REGION DE MIDI PYRENEES 2,555,887 K€ FSE - SOUTIEN AUX ACTIONS DE FORMATION S'INSCR... FSE

How are subsidies spread across project sizes?

In [9]:
from pandas import cut
bins = [0, 1000, 2000, 
        3000, 4000, 
        5000, 6000, 
        7000, 8000, 
        9000, 10000, 
        11000, 12000, 
        13000, 14000, 
        15000, 20000]
provisionned_bins = cut(amounts['amount_provisionned'], bins)
provisionned_binned = amounts.groupby(provisionned_bins).agg(sum)
provisionned_binned.index.names = ['bins']

provisionned_binned.fillna(0)
Out[9]:
amount_eligible amount_provisionned
bins
(0, 1000] 252,325 K€ 94,695 K€
(1000, 2000] 44,435 K€ 16,889 K€
(2000, 3000] 37,700 K€ 17,298 K€
(3000, 4000] 10,998 K€ 7,125 K€
(4000, 5000] 28,963 K€ 9,500 K€
(5000, 6000] 0 K€ 0 K€
(6000, 7000] 0 K€ 6,829 K€
(7000, 8000] 0 K€ 0 K€
(8000, 9000] 0 K€ 0 K€
(9000, 10000] 19,042 K€ 9,521 K€
(10000, 11000] 0 K€ 0 K€
(11000, 12000] 0 K€ 0 K€
(12000, 13000] 0 K€ 0 K€
(13000, 14000] 0 K€ 0 K€
(14000, 15000] 0 K€ 0 K€
(15000, 20000] 29,295 K€ 17,324 K€
In [10]:
from bokeh.charts import Scatter, Histogram, Bar, output_notebook, show
from bokeh.models import PrintfTickFormatter
output_notebook()

# Work-around to get the labels in order
provisionned_binned['labels'] = [
    'a (0, 1000]',
    'b (1000, 2000]',
    'c (2000, 3000]',
    'd (3000, 4000]',
    'e (4000, 5000]',
    'f (5000, 6000]',
    'g (6000, 7000]',
    'h (7000, 8000]',
    'i (8000, 9000]',
    'j (9000, 10000]',
    'k (10000, 11000]',
    'l (11000, 12000]',
    'm (12000, 13000]',
    'o (13000, 14000]',
    'p (14000, 15000]',
    'q (15000, 20000]'
]

bar = Bar(provisionned_binned, 
          values='amount_provisionned', 
          label='labels',
          ylabel='Total subsidy spending (K€)',
          title='Spending across project size (K€)')
show(bar)
Loading BokehJS ...
Out[10]:

<Bokeh Notebook handle for In[10]>

In [11]:
amounts = amounts.sort_values(by='amount_provisionned')
amounts['cumsum'] = amounts['amount_provisionned'].cumsum()
amounts['fraction_of_total'] = amounts['cumsum'] / amounts['amount_provisionned'].sum() 
amounts.head()
Out[11]:
amount_eligible amount_provisionned cumsum fraction_of_total
12 30 K€ 0 K€ 0 K€ 0 K€
504 1 K€ 1 K€ 1 K€ 0 K€
402 6 K€ 3 K€ 4 K€ 0 K€
182 14 K€ 7 K€ 11 K€ 0 K€
109 18 K€ 7 K€ 18 K€ 0 K€
In [12]:
p = Scatter(amounts, x='amount_provisionned', y='fraction_of_total')
show(p)
Out[12]:

<Bokeh Notebook handle for In[12]>

Take away point: 80% of subsidies are spent on projects < 5M€

Do recipients milk all the money they are entitled to?

In [13]:
options.display.float_format = '{:,.2f}'.format

amounts.dropna(inplace=True)
amounts.where(amounts['amount_eligible'] != 0, inplace=True)
amounts['ratio'] = amounts['amount_provisionned'] / amounts['amount_eligible'] * 100
In [14]:
s = Scatter(amounts, y='ratio', x='amount_provisionned',
            ylabel="Ratio amount programmed / eligible (%)", xlabel="Amount programmed (K€)")

show(s)
Out[14]:

<Bokeh Notebook handle for In[14]>

Break down of the subsidies by fund

In [15]:
funds = df['fund'].value_counts()
funds.index.name = 'Fund'
funds.name = 'number_of_subsidies'
funds
Out[15]:
Fund
FEDER    335
IEJ       85
FSE       81
Name: number_of_subsidies, dtype: int64
In [16]:
b = Bar(funds, ylabel='Number of grants')
show(b)
Out[16]:

<Bokeh Notebook handle for In[16]>

Funding categories

In [17]:
df['funding_category'].value_counts()
Out[17]:
CI01_064 - Processus de recherche et d'innovation dans les PME (y compris systèmes de bons, processus, conception, service et innovation sociale)                                                                                                        112
CI01_103 - Intégration durable sur le marché du travail des jeunes, en particulier ceux qui sont sans emploi et qui ne suivent ni enseignement ni formation, en mettant notamment en oeuvre la garantie pour la jeunesse                                  85
CI01_001 - Investissement productif générique dans les petites et moyennes entreprises («PME»)                                                                                                                                                            52
CI01_117 - Amélioration de l'égalité d'accès à l'apprentissage tout au long de la vie pour toutes les catégories d'âges dans un cadre formel,...                                                                                                          39
CI01_085 - Protection et amélioration de la biodiversité, protection de la nature et infrastructure verte                                                                                                                                                 28
CI01_014 - Rénovation en vue d'accroître l'efficacité énergétique du parc de logements existant, projets de démonstration et actions de soutien                                                                                                           25
CI01_104 - Emploi indépendant, entrepreneuriat et création d'entreprises, y compris les micro, petites et moyennes entreprises innovantes                                                                                                                 19
CI01_116 - Amélioration de la qualité, de l'efficacité et de l'accès à l'enseignement supérieur et équivalent afin d'accroître la participation et les niveaux de qualification, notamment des groupes défavorisés                                        12
CI01_062 - Transfert de technologies et coopération entre universités et entreprises, principalement au profit des PME                                                                                                                                    12
CI01_067 - Développement commercial des PME, soutien à l'esprit d'entreprise et à l'incubation (y compris le soutien aux entreprises issues de l'essaimage)                                                                                               11
CI01_011 - Énergies renouvelables: énergie de biomasse                                                                                                                                                                                                     9
CI01_012 - Autres types d'énergies renouvelables (y compris l'énergie hydroélectrique, géothermique et marine) et intégration des énergies renouvelables                                                                                                   7
CI01_060 - Activités de recherche et d'innovation dans les centres de recherche publics et les centres de compétence, y compris la mise en réseau                                                                                                          6
CI01_049 - Infrastructures éducatives de l'enseignement supérieur                                                                                                                                                                                          5
CI01_058 - Infrastructures de recherche et d'innovation (publiques)                                                                                                                                                                                        4
CI01_122 - Évaluation et études                                                                                                                                                                                                                            3
CI01_121 - Préparation, mise en oeuvre, suivi et contrôle                                                                                                                                                                                                  3
CI01_091 - Développement et promotion du potentiel touristique des espaces naturels                                                                                                                                                                        3
CI01_080 - Services et applications en matière d'inclusion en ligne, d'accessibilité en ligne, d'apprentissage et d'éducation en ligne, culture numérique                                                                                                  2
CI01_023 - Mesures environnementales visant à réduire et/ou à éviter les émissions de gaz à effet de serre (y compris le traitement et le stockage du méthane et le compostage)                                                                            2
CI01_123 - Information et communication                                                                                                                                                                                                                    2
CI01_013 - Rénovation en vue d'accroître l'efficacité énergétique des infrastructures publiques, projets de démonstration et actions de soutien                                                                                                            2
CI01_089 - Réhabilitation des sites industriels et des terrains contaminés                                                                                                                                                                                 1
CI01_097 - Initiatives de développement local menées par les acteurs locaux dans les zones urbaines et rurales                                                                                                                                             1
CI01_082 - Services et applications TIC pour les PME (y compris le commerce électronique, le e-Business et les processus d'entreprise en réseau), les laboratoires vivants, les entrepreneurs web et les start-ups en matière de TIC                       1
CI01_084 - Prévention et contrôle intégrés de la pollution (PCIP)                                                                                                                                                                                          1
CI01_074 - Développement et promotion des actifs touristiques commerciaux dans les PME                                                                                                                                                                     1
CI01_047 - TIC: Réseau haut débit à très grande vitesse (accès/boucle locale; >/= 100 Mbps)                                                                                                                                                                1
CI01_078 - Services et applications d'administration en ligne (y compris passation des marchés publics en ligne, mesures dans le domaine des TIC soutenant la réforme de l'administration publique, mesures dans le domaine de la cybersécurité.....)      1
CI01_118 - Amélioration de l'utilité des systèmes d'éducation et de formation pour le marché du travail, passage plus aisé du système éducatif au monde du travail et amélioration du travail,...                                                          1
CI01_046 - TIC: Réseau haut débit à grande vitesse (accès/boucle locale; >/= 30 Mbps)                                                                                                                                                                      1
CI01_004 - Investissement productif lié à la coopération entre les grandes entreprises et les PME pour le développement de produits et services des TIC                                                                                                    1
CI01_036 - Transports multimodaux                                                                                                                                                                                                                          1
CI01_017 - Gestion des déchets ménagers (y compris les mesures de réduction, tri et recyclage)                                                                                                                                                             1
Name: funding_category, dtype: int64
In [18]:
df['short_category'] = df['funding_category'].apply(lambda x: str(x)[:8])
b = Bar(df, values='amount_provisionned', agg='sum', stack='short_category', label='fund', legend='top_right')
show(b)
Out[18]:

<Bokeh Notebook handle for In[18]>

Geographical spread

In [19]:
df['departements'] = df['recipient_postcode'].apply(str).apply(lambda x: x[:2])
geo = df.groupby('departements').agg('sum')
geo.dropna(inplace=True)
geo.index.names = ['departements']
geo.info()
<class 'pandas.core.frame.DataFrame'>
Index: 31 entries, 01 to na
Data columns (total 2 columns):
amount_eligible        31 non-null float64
amount_provisionned    31 non-null float64
dtypes: float64(2)
memory usage: 744.0+ bytes

Top districts

In [20]:
geo.sort_values(by='amount_eligible', inplace=True, ascending=False)
geo.head()
Out[20]:
amount_eligible amount_provisionned
departements
31 146,184,382.43 65,782,656.89
97 46,443,958.00 31,551,892.00
81 39,069,621.93 8,682,240.25
09 28,997,332.36 7,170,641.11
25 24,643,177.61 10,843,441.72
In [22]:
# Test the map
from folium import Map, GeoJson
import pandas as pd

departements = r'/home/loic/repos/eijc16/departements.json'


#Let Folium determine the scale
france = Map(location=[47, 2], zoom_start=6)

GeoJson(open(departements), name='geojson').add_to(france)
france
Out[22]:
In [23]:
geo.reset_index(level=0, inplace=True)
geo.sort_values(by='departements').head()
Out[23]:
departements amount_eligible amount_provisionned
15 01 2,681,647.00 760,750.65
21 03 1,545,000.00 84,600.00
3 09 28,997,332.36 7,170,641.11
8 12 15,162,755.43 3,563,102.46
27 13 228,275.00 132,285.36
In [24]:
from pandas import DataFrame, concat

# fill in missing departements (for folium)
for dep in [str(n).zfill(2) for n in range(1, 96)]:
    if dep not in geo['departements'].values:
        missing_district = DataFrame({'departements': [str(dep)], 'amount_eligible': [0], 'amount_provisionned': [0]})
        geo = concat([geo, missing_district])
        
geo.where(geo['departements'] != 'na', inplace=True)
geo.dropna(inplace=True)
geo.departements.values
Out[24]:
array(['31', '97', '81', '09', '25', '63', '87', '65', '12', '32', '46',
       '90', '39', '70', '01', '82', '44', '38', '20', '19', '03', '75',
       '72', '85', '93', '51', '13', '69', '21', '71', '02', '04', '05',
       '06', '07', '08', '10', '11', '14', '15', '16', '17', '18', '22',
       '23', '24', '26', '27', '28', '29', '30', '33', '34', '35', '36',
       '37', '40', '41', '42', '43', '45', '47', '48', '49', '50', '52',
       '53', '54', '55', '56', '57', '58', '59', '60', '61', '62', '64',
       '66', '67', '68', '73', '74', '76', '77', '78', '79', '80', '83',
       '84', '86', '88', '89', '91', '92', '94', '95'], dtype=object)
In [25]:
# Corsica: dude this map is picky
corsica = DataFrame({'departements': ['2A', '2B'], 'amount_eligible': [0, 0], 'amount_provisionned': [0, 0]})
geo = concat([geo, corsica])
In [26]:
geo.tail()
Out[26]:
amount_eligible amount_provisionned departements
0 0.00 0.00 92
0 0.00 0.00 94
0 0.00 0.00 95
0 0.00 0.00 2A
1 0.00 0.00 2B
In [27]:
from math import log10

geo['amount_provisionned'] = geo['amount_provisionned'].replace(0, 0.1)
geo['scale'] = geo['amount_provisionned'].apply(log10)

france.choropleth(
    departements, 
    data=geo,
    columns=['departements', 'scale'],
    key_on='properties.code',
    fill_color='YlGn', fill_opacity=0.7, line_opacity=0.2,
    legend_name='Subsidies')

france
/home/loic/.anaconda/lib/python3.5/site-packages/ipykernel/__main__.py:12: FutureWarning: 'threshold_scale' default behavior has changed. Now you get a linear scale between the 'min' and the 'max' of your data. To get former behavior, use folium.utilities.split_six.
Out[27]:
In [ ]: